package com.wx.house.manager.api.permission.controller;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.wx.house.manager.api.capital.service.SysAdminDetailService;
import com.wx.house.manager.api.permission.service.SysAdminService;
import com.wx.house.manager.api.permission.text.AdminLogin;
import com.wx.house.manager.api.permission.text.AdminLoginPame;
import com.wx.house.manager.api.permission.text.LoginPhonePame;
import com.wx.house.manager.api.permission.text.PhonePanme;
import com.wx.house.manager.api.permission.text.loginPwdPame;

import org.apache.commons.lang3.ObjectUtils;
import org.apache.shiro.crypto.hash.Sha256Hash;
import org.springframework.beans.BeanUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.wx.house.manager.api.permission.service.SysMenusService;
import com.wx.house.manager.api.permission.service.SysRoleMenuService;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.wx.house.common.assembly.alysms.MessageUtil;
import com.wx.house.common.base.BaseController;
import com.wx.house.common.base.Constant;
import com.wx.house.common.exception.BusinessException;
import com.wx.house.common.util.IDUtils;
import com.wx.house.common.util.JwtUtils;
import com.wx.house.common.util.StringUtil;
import com.wx.house.core.pojo.base.Result;
import com.wx.house.core.pojo.po.ECollectFees;
import com.wx.house.core.pojo.po.SysAdmin;
import com.wx.house.core.pojo.po.SysMenus;
import com.wx.house.core.pojo.vo.PermissionMenuVo;
import com.wx.house.core.pojo.vo.SysMenusVo;

import io.jsonwebtoken.Claims;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@RestController
@RequestMapping("AdminLogin/")
@Api(value = "管理员登录", tags = "模块:管理员登录管理")
@Slf4j
public class AdminLoginController extends BaseController {
	@Resource
	private SysAdminService sysAdminService;
	@Resource
	private SysRoleMenuService sysRoleMenuService;
	@Resource
	private SysMenusService sysMenusService;
	@Resource
	private SysAdminDetailService sysAdminCapitalService;
	
	@GetMapping("Login/")
	@ApiOperation(value = "用户登录", notes = "用户登录")
	public Result LoginAdmin(AdminLogin adminLogin, HttpServletResponse response) {
		// 根据账号(手机号)查询记录
		SysAdmin sysAdmin = sysAdminService.loginAdmin(adminLogin.getLoginName());
		try {
			if (StringUtil.isEmpty(sysAdmin)) {
				return Result.error("账号不存在");
			}
			if (sysAdmin.getState()==1) {
				return Result.error("账号已被封禁");
			}
			// 判断用户密码是否正确
			if (comparePassword(adminLogin.getLoginPwd(), sysAdmin.getLoginPwd(), sysAdmin.getSafeKey())) {
				// 调用SetUpToken,获取token
				String token = SetUpToken(sysAdmin.getId(), sysAdmin.getAccountType().toString(), sysAdmin.getMAccId(),
						sysAdmin.getLevel(), sysAdmin.getRealName());
				// token返回
				response.setHeader("authorization", token);
				Set<SysMenusVo> userMenuSet ;
				if(sysAdmin.getAccountType()==0) {
					userMenuSet = sysMenusService.findAdminMenu();
				}else{
					userMenuSet = sysMenusService.findMenuByUserId(sysAdmin.getId());
				}
				//方法内部会对以上数据去重
				List<SysMenusVo> menuList = listMenus(userMenuSet);
				
				List<PermissionMenuVo> PermMenuList=sysMenusService.selectMenuPermission(sysAdmin.getId());
				List<PermissionMenuVo> listPid=new ArrayList<PermissionMenuVo>();
				for (int i = 0; i < PermMenuList.size(); i++) {
						PermissionMenuVo selectIdPermiss=sysMenusService.listPidMenus(PermMenuList.get(i).getPId());
						selectIdPermiss.setPermissionId(PermMenuList.get(i).getPermissionId());
						listPid.add(selectIdPermiss);
				}
				//sysAdminDto.setSysMenus(list);
				// 更新用户登录时间，ip
				sysAdminService.updateAdminip(sysAdmin.getId(), this.ip(), new Date());
				return Result.success(new UserDetails(sysAdmin, menuList,listPid));
			} else {
				return Result.error("密码错误");
			}
		} catch (Exception e) {
			e.printStackTrace();
			throw new BusinessException("登录失败");
		}
	}

	@GetMapping("getPhone/")
	@ApiImplicitParams({
			@ApiImplicitParam(name = "phone", value = "电话号码", required = true, dataType = "String", paramType = "query") })
	@ApiOperation(value = "判断手机号是否存在", notes = "判断手机号是否存在")
	public Result getPhone(String phone) {
		SysAdmin admin = sysAdminService.loginPhone(phone);
		if (StringUtil.isNotEmpty(admin)) {
			return Result.success();
		}
		return Result.error("手机号不存在");
	}
	
	@GetMapping("getCode/")
	@ApiImplicitParams({
			@ApiImplicitParam(name = "phone", value = "电话号码", required = true, dataType = "String", paramType = "query"),
			@ApiImplicitParam(name = "code", value = "验证码", required = true, dataType = "String", paramType = "query") 
	})
	@ApiOperation(value = "手机短信验证", notes = "手机短信验证")
	public Result getCode(String phone,String code) {
		try {
			String validCode = this.redisUtil().get(String.format(MessageUtil.SEND_CODE, phone));
			if (validCode == null) {
				return Result.error("还未发送验证码");
			}
			if (!validCode.equals(code)) {
				return Result.error("验证码错误");
			}
			return Result.success();
		} catch (Exception e) {
			  throw new BusinessException("验证失败");
		}
	}
	
	@GetMapping("getValiCode/")
	@ApiImplicitParams({
			@ApiImplicitParam(name = "phone", value = "电话号码", required = true, dataType = "String", paramType = "query"),
			@ApiImplicitParam(name = "Valicode", value = "图片验证码", required = true, dataType = "String", paramType = "query") 
	})
	@ApiOperation(value = "手机图片验证码", notes = "手机图片验证码")
	public Result getValiCode(String phone,String Valicode) {
		try {
			SysAdmin admin = sysAdminService.loginPhone(phone);
			if(ObjectUtils.isNotEmpty(admin)) {
				if(admin.getState()==1)
				{
					return Result.error("账号被禁用");
				}
			}else {
				return Result.error("账号不存在");
			}
			String ip =this.ip();
			String code = redisUtil().get(String.format(VALIDATECODE, ip));
			if(!code.equals(Valicode)) {
				return Result.error("验证码错误");
			}
			return Result.success();
		} catch (Exception e) {
			  throw new BusinessException("验证失败");
		}
	}
	
	@PostMapping("LoginPhone/")
	@ApiOperation(value = "短信登录", notes = "短信登录")
	public Result LoginPhone(@RequestBody @Validated LoginPhonePame loginPhonePame, HttpServletResponse response) {
		try {
			// 验证 验证码
			String validCode = this.redisUtil().get(String.format(MessageUtil.SEND_CODE, loginPhonePame.getPhone()));
			if (validCode == null) return Result.error("还未发送验证码");
			if (!validCode.equals(loginPhonePame.getCode())) {
				return Result.error("验证码错误");
			}
			// 根据手机号查询记录
			SysAdmin sysAdmin = sysAdminService.loginAdmin(loginPhonePame.getPhone());
			if (ObjectUtils.isNotEmpty(sysAdmin)) {
				if (sysAdmin.getState() == 1) {
					return Result.error("账号被禁用");
				}
			} else {
				// 验证码正确，创建账号
				String uuid = String.valueOf(IDUtils.genItemId(16));
				sysAdmin = sysAdminService.addAdminAndDetail(loginPhonePame.getPhone(),uuid );
				// 创建管理员表
				if (ObjectUtils.isEmpty(sysAdmin)) {
					return Result.error("创建账户失败！请联系管理员");
				}
			}
			// 调用SetUpToken,获取token
			String token = SetUpToken(sysAdmin.getId(), sysAdmin.getAccountType().toString(), sysAdmin.getMAccId(),
					sysAdmin.getLevel(), sysAdmin.getRealName());
			// token返回
			response.setHeader("authorization", token);
			// 调用getTree1
			Set<SysMenusVo> userMenuSet;
			if(sysAdmin.getAccountType()==0) {
				userMenuSet = sysMenusService.findAdminMenu();
			}else{
				userMenuSet = sysMenusService.findMenuByUserId(sysAdmin.getId());
			}
			//方法内部会对以上数据去重
			List<SysMenusVo> menuList = listMenus(userMenuSet);
			// 判断账户里面密码是否存在
			String a=null;
			if (!StringUtil.isEmpty(sysAdmin.getLoginPwd())) {
				 a= 0+"";
			} else {
				 a= 1+"";
			}
			List<PermissionMenuVo> PermMenuList=sysMenusService.selectMenuPermission(sysAdmin.getId());
			List<PermissionMenuVo> listPid=new ArrayList<PermissionMenuVo>();
			for (int i = 0; i < PermMenuList.size(); i++) {
					PermissionMenuVo selectIdPermiss=sysMenusService.listPidMenus(PermMenuList.get(i).getPId());
					selectIdPermiss.setPermissionId(PermMenuList.get(i).getPermissionId());
					listPid.add(selectIdPermiss);
			}
			return Result.success(new CodeUserDetails(sysAdmin, menuList, a,listPid));
		} catch (Exception e) {
			log.error(e.getMessage());
			throw new BusinessException("登录失败");
		}
	}
	@PutMapping("updateLogAdmin/")
	@ApiOperation(value = "设置密码", notes = "设置密码")
	public Result updateLogAdmin(@RequestBody @Validated loginPwdPame loginPwdPame) {
		try {
			//获取循环次数
			String HASHITERATIONS = this.config("common.HASHITERATIONS");
			if (StringUtil.isNotEmpty(HASHITERATIONS)) {
				HASHITERATIONS = "1024";
			}
			//随机生成safakey
			Integer safeKey = IDUtils.getIntegerSafeKey();
			//根据电话号码查用户
			SysAdmin sysAdmin = sysAdminService.loginAdmin(loginPwdPame.getPhone());
			if (ObjectUtils.isEmpty(sysAdmin)) {
				return Result.error("用户不存在");
			}
			else {
				sysAdminService.updateAdminLog(sysAdmin.getId(), new Sha256Hash(loginPwdPame.getLoginPwd(), safeKey.toString(), Integer.parseInt(HASHITERATIONS)).toString(), safeKey);
			}
			return Result.success();
		} catch (Exception e) {
			throw new BusinessException("设置密码失败");
		}
	}
	
	
	@PutMapping("updateLog/")
	@ApiOperation(value = "修改密码", notes = "修改密码")
	public Result updateAdminLog(@RequestBody @Validated AdminLoginPame adminLoginPame, HttpServletRequest request) {
		try {
			String token = request.getHeader("authorization");
			// step1. 解析token
			Claims claims = JwtUtils.builder().token(token).build().claims();
			// step2. 从token获取参数,id
			String adminid = claims.get("id").toString();
			// step3. 获取循环次数
			String HASHITERATIONS = this.config("common.HASHITERATIONS");
			if (StringUtil.isNotEmpty(HASHITERATIONS)) {
				HASHITERATIONS = "1024";
			}
			// step4. 随机生成safakey
			Integer safeKey = IDUtils.getIntegerSafeKey();
			// step5.根据toke查询用户以前密码
			SysAdmin sysAdmin = sysAdminService.loginAdminId(adminid);
			if (comparePassword(adminLoginPame.getUsedPwd(), sysAdmin.getLoginPwd(), sysAdmin.getSafeKey())) {
				String LoginPwd = new Sha256Hash(adminLoginPame.getNewPwd(), safeKey.toString(),
						Integer.parseInt(HASHITERATIONS)).toString();
				sysAdminService.updateAdminLog(adminid, LoginPwd, safeKey);
			} else {
				return Result.error("原密码错误");
			}
			return Result.success();
		} catch (Exception e) {
			throw new BusinessException("修改密码失败");
		}
	}
	
	@PutMapping("updatePhone/")
	@ApiOperation(value = "修改手机号", notes = "修改手机号")
	public Result updatePhone(@RequestBody @Validated PhonePanme phonePanme, HttpServletRequest request) {
		try {
			// 验证 验证码
			String validCode = this.redisUtil().get(String.format(MessageUtil.SEND_CODE, phonePanme.getPhone()));
			if (validCode == null)
				throw new BusinessException("还未发送验证码");
			if (!validCode.equals(phonePanme.getCode())) {
				return Result.error("验证码错误");
			}
			String token = request.getHeader("authorization");
			// step1. 解析token
			Claims claims = JwtUtils.builder().token(token).build().claims();
			// step2. 从token获取参数,id
			String adminid = claims.get("id").toString();
			sysAdminService.updateAdminPhone(adminid, phonePanme.getPhone());
			return Result.success();
		} catch (Exception e) {
			throw new BusinessException("修改手机号失败");
		}
	}

	@ApiOperation(value = "退出登陆", notes = "退出登陆")
	@GetMapping("logout/")
	public Result logout(HttpServletRequest request) {
		String token = request.getHeader("authorization");
		// step1. 解析token
		Claims claims = JwtUtils.builder().token(token).build().claims();
		// step2. 从token获取参数,id
		String id = claims.get("id").toString();
		// step3. 从token获取参数,mAccId
		String mAccId = claims.get("mAccId").toString();
		if ("0".equals(mAccId)) {
			// redis里删除用户信息
			redisUtil().delete(String.format(Constant.REDIS_LOGIN_TOKEN, id, ""));
		} else {
			redisUtil().delete(String.format(Constant.REDIS_LOGIN_TOKEN, mAccId, id));
		}
		return Result.success(null);
	}
	
	@Data
	@AllArgsConstructor
	@NoArgsConstructor
	@ApiModel
	@JsonInclude(JsonInclude.Include.NON_EMPTY)
	private static class UserDetails {
		@ApiModelProperty(value = "用户信息", name = "details")
		private SysAdmin details;
		@ApiModelProperty(value = "用户菜单", name = "menuList")
		private List<SysMenusVo> menuList;
		@ApiModelProperty(value = "用户拥有的菜单权限", name = "PermMenuList")
		private List<PermissionMenuVo> PermMenuList;
	}
	@Data
	@AllArgsConstructor
	@NoArgsConstructor
	@ApiModel
	@JsonInclude(JsonInclude.Include.NON_EMPTY)
	private static class CodeUserDetails {
		@ApiModelProperty(value = "用户信息", name = "details")
		private SysAdmin details;
		@ApiModelProperty(value = "用户菜单", name = "menuList")
		private List<SysMenusVo> menuList;
		@ApiModelProperty(value = "0有密码 1没有密码", name = "whether")
		private String  whether;
		@ApiModelProperty(value = "用户拥有的菜单权限", name = "PermMenuList")
		private List<PermissionMenuVo> PermMenuList;
	}
	
	
	private List<SysMenusVo> listMenus(Set<SysMenusVo> menuSet) {
		if (menuSet.isEmpty()) throw new RuntimeException("用户所在角色为拥有任何菜单链接，无法使用");
		//填充map索引
		Map<String, SysMenusVo> menuMap = new HashMap<>();
		//将链接存入map
		menuSet.forEach(e -> menuMap.put(e.getId(), e));
		
		//获取当前tempList里的数据的父类
		List<SysMenusVo> tempList = new ArrayList<>(menuSet);
		while (getIsAllRootNode(tempList)) {//如果不是全部节点pid都是ROOT
			List<SysMenusVo> tempList2 = sysMenusService.findAllByIdList(tempList
					                                                   .stream()
					                                                   .map(SysMenusVo::getPId)
					                                                   .filter(e -> !e.equals("0"))
					                                                   .collect(Collectors.toList()));
			tempList = tempList2;
			tempList2.forEach(e -> menuMap.put(e.getId(), e));
		}
		Map<String, SysMenusVo> resultMenuMap = new HashMap<>();
		//装载所有子节点
		menuMap.forEach((k, v) -> {
			SysMenusVo parentNode = menuMap.get(v.getPId());
			if (!ObjectUtils.isEmpty(parentNode)) {
				
				SysMenusVo newParentNode = new SysMenusVo();
				BeanUtils.copyProperties(parentNode, newParentNode);
				
				//加入父节点并且根据MenuOrder排序
				List<SysMenusVo> childList = newParentNode.getChild();
				childList.add(v);
				newParentNode.setChild(childList.parallelStream().sorted(Comparator.comparing(SysMenusVo::getMenuOrder)).collect(Collectors.toList()));
				
				resultMenuMap.put(newParentNode.getId(), newParentNode);
			}
		});
		
		//将数据存到List用于返回
		List<SysMenusVo> resultMenuList = new ArrayList<>();
		resultMenuMap.forEach((k, v) -> {
			if (v.getPId().equals("0"))
				resultMenuList.add(v);
		});
		return resultMenuList;
	}
	private Boolean getIsAllRootNode(List<SysMenusVo> menuList) {
		return menuList
				       .stream()//所有节点的pid都是0
				       .filter(value -> value.getPId().equals("0"))
				       .count() != menuList.size();
	}
	/***
	 * 存token到redis
	 * 
	 * @param id
	 * @param accountType
	 * @param mAccId
	 * @param level
	 * @param realName
	 * @return
	 */
	public String SetUpToken(String id, String accountType, String mAccId, String level, String realName) {
		// 获取token
		// step1. 参数列表
		Map<String, String> paramList = new HashMap<>();
		// 存入id
		paramList.put("id", id);
		// 存入账号类型
		paramList.put("accountType", accountType);
		// 存入主账号id
		paramList.put("mAccId", mAccId);
		// 存入等级
		paramList.put("level", level);
		// 存入名字
		paramList.put("realName", realName);
		// step2. 构建tokenUtil
		JwtUtils build = JwtUtils.builder().paramList(paramList).build();
		// step3. 通过token方法获取token
		String token = build.token();
		// step5. 存入redis判断是主账号，还是子账户
		if ("0".equals(mAccId)) {
			redisUtil().set(String.format(Constant.REDIS_LOGIN_TOKEN, id, ""), token, 3L, TimeUnit.HOURS);
		} else {
			redisUtil().set(String.format(Constant.REDIS_LOGIN_TOKEN, mAccId, id), token, 3L, TimeUnit.HOURS);
		}
		return token;
	}
}